home *** CD-ROM | disk | FTP | other *** search
/ MacAddict 83 / MacAddict_083_2003-07.iso / mac / Software / Development / VLC Source 0.5.3.dmg / src / playlist / playlist.c
C/C++ Source or Header  |  2003-04-07  |  24KB  |  764 lines

  1. /*****************************************************************************
  2.  * playlist.c : Playlist management functions
  3.  *****************************************************************************
  4.  * Copyright (C) 1999-2001 VideoLAN
  5.  * $Id: playlist.c,v 1.34 2003/03/18 00:49:14 gbazin Exp $
  6.  *
  7.  * Authors: Samuel Hocevar <sam@zoy.org>
  8.  *
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 of the License, or
  12.  * (at your option) any later version.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  22.  *****************************************************************************/
  23. #include <stdlib.h>                                      /* free(), strtol() */
  24. #include <stdio.h>                                              /* sprintf() */
  25. #include <string.h>                                            /* strerror() */
  26.  
  27. #include <vlc/vlc.h>
  28.  
  29. #include "stream_control.h"
  30. #include "input_ext-intf.h"
  31.  
  32. #include "vlc_playlist.h"
  33.  
  34. #define PLAYLIST_FILE_HEADER_0_5  "# vlc playlist file version 0.5"
  35. #ifdef WIN32
  36. #   define PLAYLIST_FILE_EOL "\r\n"
  37. #else
  38. #   define PLAYLIST_FILE_EOL "\n"
  39. #endif
  40.  
  41. /*****************************************************************************
  42.  * Local prototypes
  43.  *****************************************************************************/
  44. static void RunThread ( playlist_t * );
  45. static void SkipItem  ( playlist_t *, int );
  46. static void PlayItem  ( playlist_t * );
  47.  
  48. static void Poubellize ( playlist_t *, input_thread_t * );
  49.  
  50. /*****************************************************************************
  51.  * playlist_Create: create playlist
  52.  *****************************************************************************
  53.  * Create a playlist structure.
  54.  *****************************************************************************/
  55. playlist_t * __playlist_Create ( vlc_object_t *p_parent )
  56. {
  57.     playlist_t *p_playlist;
  58.     vlc_value_t     val;
  59.  
  60.     /* Allocate structure */
  61.     p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
  62.     if( !p_playlist )
  63.     {
  64.         msg_Err( p_parent, "out of memory" );
  65.         return NULL;
  66.     }
  67.  
  68.     var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
  69.     val.b_bool = VLC_TRUE;
  70.     var_Set( p_playlist, "intf-change", val );
  71.  
  72.     p_playlist->p_input = NULL;
  73.     p_playlist->i_status = PLAYLIST_STOPPED;
  74.     p_playlist->i_index = -1;
  75.     p_playlist->i_size = 0;
  76.     p_playlist->pp_items = NULL;
  77.  
  78.     if( vlc_thread_create( p_playlist, "playlist", RunThread,
  79.                            VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
  80.     {
  81.         msg_Err( p_playlist, "cannot spawn playlist thread" );
  82.         vlc_object_destroy( p_playlist );
  83.         return NULL;
  84.     }
  85.  
  86.     /* The object has been initialized, now attach it */
  87.     vlc_object_attach( p_playlist, p_parent );
  88.  
  89.     return p_playlist;
  90. }
  91.  
  92. /*****************************************************************************
  93.  * playlist_Destroy: destroy the playlist
  94.  *****************************************************************************
  95.  * Delete all items in the playlist and free the playlist structure.
  96.  *****************************************************************************/
  97. void playlist_Destroy( playlist_t * p_playlist )
  98. {
  99.     p_playlist->b_die = 1;
  100.  
  101.     vlc_thread_join( p_playlist );
  102.  
  103.     var_Destroy( p_playlist, "intf-change" );
  104.  
  105.     vlc_object_destroy( p_playlist );
  106. }
  107.  
  108. /*****************************************************************************
  109.  * playlist_Add: add an item to the playlist
  110.  *****************************************************************************
  111.  * Add an item to the playlist at position i_pos. If i_pos is PLAYLIST_END,
  112.  * add it at the end regardless of the playlist current size.
  113.  *****************************************************************************/
  114. int playlist_Add( playlist_t *p_playlist, const char * psz_target,
  115.                                           int i_mode, int i_pos )
  116. {
  117.     playlist_item_t * p_item;
  118.  
  119.     p_item = malloc( sizeof( playlist_item_t ) );
  120.     if( p_item == NULL )
  121.     {
  122.         msg_Err( p_playlist, "out of memory" );
  123.     }
  124.  
  125.     p_item->psz_name = strdup( psz_target );
  126.     p_item->psz_uri  = strdup( psz_target );
  127.     p_item->i_type = 0;
  128.     p_item->i_status = 0;
  129.     p_item->b_autodeletion = VLC_FALSE;
  130.  
  131.     return playlist_AddItem( p_playlist, p_item, i_mode, i_pos );
  132. }
  133.  
  134.  
  135. int playlist_AddItem( playlist_t *p_playlist, playlist_item_t * p_item,
  136.                 int i_mode, int i_pos)
  137. {
  138.     vlc_value_t     val;
  139.  
  140.     vlc_mutex_lock( &p_playlist->object_lock );
  141.  
  142.     /*
  143.      * CHECK_INSERT : checks if the item is already enqued before
  144.      * enqueing it
  145.      */
  146.     if ( i_mode & PLAYLIST_CHECK_INSERT )
  147.     {
  148.          int j;
  149.  
  150.          if ( p_playlist->pp_items )
  151.          {
  152.              for ( j = 0; j < p_playlist->i_size; j++ )
  153.              {
  154.                  if ( !strcmp( p_playlist->pp_items[j]->psz_uri, p_item->psz_uri ) )
  155.                  {
  156.                       if( p_item->psz_name )
  157.                       {
  158.                           free( p_item->psz_name );
  159.                       }
  160.                       if( p_item->psz_uri )
  161.                       {
  162.                           free( p_item->psz_uri );
  163.                       }
  164.                       free( p_item );
  165.                       vlc_mutex_unlock( &p_playlist->object_lock );
  166.                       return 0;
  167.                  }
  168.              }
  169.          }
  170.          i_mode &= ~PLAYLIST_CHECK_INSERT;
  171.          i_mode |= PLAYLIST_APPEND;
  172.     }
  173.  
  174.  
  175.     msg_Dbg( p_playlist, "adding playlist item ´ %s ª", p_item->psz_name );
  176.  
  177.     /* Create the new playlist item */
  178.  
  179.  
  180.     /* Do a few boundary checks and allocate space for the item */
  181.     if( i_pos == PLAYLIST_END )
  182.     {
  183.         if( i_mode & PLAYLIST_INSERT )
  184.         {
  185.             i_mode &= ~PLAYLIST_INSERT;
  186.             i_mode |= PLAYLIST_APPEND;
  187.         }
  188.  
  189.         i_pos = p_playlist->i_size - 1;
  190.     }
  191.  
  192.     if( !(i_mode & PLAYLIST_REPLACE)
  193.          || i_pos < 0 || i_pos >= p_playlist->i_size )
  194.     {
  195.         /* Additional boundary checks */
  196.         if( i_mode & PLAYLIST_APPEND )
  197.         {
  198.             i_pos++;
  199.         }
  200.  
  201.         if( i_pos < 0 )
  202.         {
  203.             i_pos = 0;
  204.         }
  205.         else if( i_pos > p_playlist->i_size )
  206.         {
  207.             i_pos = p_playlist->i_size;
  208.         }
  209.  
  210.         INSERT_ELEM( p_playlist->pp_items,
  211.                      p_playlist->i_size,
  212.                      i_pos,
  213.                      p_item );
  214.  
  215.         if( p_playlist->i_index >= i_pos )
  216.         {
  217.             p_playlist->i_index++;
  218.         }
  219.     }
  220.     else
  221.     {
  222.         /* i_mode == PLAYLIST_REPLACE and 0 <= i_pos < p_playlist->i_size */
  223.         if( p_playlist->pp_items[i_pos]->psz_name )
  224.         {
  225.             free( p_playlist->pp_items[i_pos]->psz_name );
  226.         }
  227.         if( p_playlist->pp_items[i_pos]->psz_uri )
  228.         {
  229.             free( p_playlist->pp_items[i_pos]->psz_uri );
  230.         }
  231.         /* XXX: what if the item is still in use? */
  232.         free( p_playlist->pp_items[i_pos] );
  233.         p_playlist->pp_items[i_pos] = p_item;
  234.     }
  235.  
  236.     if( i_mode & PLAYLIST_GO )
  237.     {
  238.         p_playlist->i_index = i_pos;
  239.         if( p_playlist->p_input )
  240.         {
  241.             input_StopThread( p_playlist->p_input );
  242.         }
  243.         p_playlist->i_status = PLAYLIST_RUNNING;
  244.     }
  245.  
  246.     vlc_mutex_unlock( &p_playlist->object_lock );
  247.  
  248.     val.b_bool = VLC_TRUE;
  249.     var_Set( p_playlist, "intf-change", val );
  250.  
  251.     return 0;
  252. }
  253.  
  254. /*****************************************************************************
  255.  * playlist_Delete: delete an item from the playlist
  256.  *****************************************************************************
  257.  * Delete the item in the playlist with position i_pos.
  258.  *****************************************************************************/
  259. int playlist_Delete( playlist_t * p_playlist, int i_pos )
  260. {
  261.     vlc_value_t     val;
  262.     vlc_mutex_lock( &p_playlist->object_lock );
  263.  
  264.     if( i_pos >= 0 && i_pos < p_playlist->i_size )
  265.     {
  266.         msg_Dbg( p_playlist, "deleting playlist item ´ %s ª",
  267.                              p_playlist->pp_items[i_pos]->psz_name );
  268.  
  269.         if( p_playlist->pp_items[i_pos]->psz_name )
  270.         {
  271.             free( p_playlist->pp_items[i_pos]->psz_name );
  272.         }
  273.         if( p_playlist->pp_items[i_pos]->psz_uri )
  274.         {
  275.             free( p_playlist->pp_items[i_pos]->psz_uri );
  276.         }
  277.  
  278.         /* XXX: what if the item is still in use? */
  279.         free( p_playlist->pp_items[i_pos] );
  280.  
  281.         if( i_pos <= p_playlist->i_index )
  282.         {
  283.             p_playlist->i_index--;
  284.         }
  285.  
  286.         /* Renumber the playlist */
  287.         REMOVE_ELEM( p_playlist->pp_items,
  288.                      p_playlist->i_size,
  289.                      i_pos );
  290.     }
  291.  
  292.     vlc_mutex_unlock( &p_playlist->object_lock );
  293.  
  294.     val.b_bool = VLC_TRUE;
  295.     var_Set( p_playlist, "intf-change", val );
  296.  
  297.     return 0;
  298. }
  299.  
  300. /*****************************************************************************
  301.  * playlist_Move: move an item in the playlist
  302.  *****************************************************************************
  303.  * Move the item in the playlist with position i_pos before the current item
  304.  * at position i_newpos.
  305.  *****************************************************************************/
  306. int playlist_Move( playlist_t * p_playlist, int i_pos, int i_newpos)
  307. {
  308.     vlc_value_t     val;
  309.     vlc_mutex_lock( &p_playlist->object_lock );
  310.  
  311.     /* take into account that our own row disappears. */
  312.     if ( i_pos < i_newpos ) i_newpos--;
  313.  
  314.     if( i_pos >= 0 && i_newpos >=0 && i_pos <= p_playlist->i_size 
  315.                      && i_newpos <= p_playlist->i_size )
  316.     {
  317.         playlist_item_t * temp;
  318.  
  319.         msg_Dbg( p_playlist, "moving playlist item ´ %s ª",
  320.                              p_playlist->pp_items[i_pos]->psz_name );
  321.  
  322.         if( i_pos == p_playlist->i_index )
  323.         {
  324.             p_playlist->i_index = i_newpos;
  325.         }
  326.         else if( i_pos > p_playlist->i_index && i_newpos <= p_playlist->i_index )
  327.         {
  328.             p_playlist->i_index++;
  329.         }
  330.         else if( i_pos < p_playlist->i_index && i_newpos >= p_playlist->i_index )
  331.         {
  332.             p_playlist->i_index--;
  333.         }
  334.  
  335.         if ( i_pos < i_newpos )
  336.         {
  337.             temp = p_playlist->pp_items[i_pos];
  338.             while ( i_pos < i_newpos )
  339.             {
  340.                 p_playlist->pp_items[i_pos] = p_playlist->pp_items[i_pos+1];
  341.                 i_pos++;
  342.             }
  343.             p_playlist->pp_items[i_newpos] = temp;
  344.         }
  345.         else if ( i_pos > i_newpos )
  346.         {
  347.             temp = p_playlist->pp_items[i_pos];
  348.             while ( i_pos > i_newpos )
  349.             {
  350.                 p_playlist->pp_items[i_pos] = p_playlist->pp_items[i_pos-1];
  351.                 i_pos--;
  352.             }
  353.             p_playlist->pp_items[i_newpos] = temp;
  354.         }
  355.     }
  356.  
  357.     vlc_mutex_unlock( &p_playlist->object_lock );
  358.  
  359.     val.b_bool = VLC_TRUE;
  360.     var_Set( p_playlist, "intf-change", val );
  361.  
  362.     return 0;
  363. }
  364.  
  365. /*****************************************************************************
  366.  * playlist_Command: do a playlist action
  367.  *****************************************************************************
  368.  *
  369.  *****************************************************************************/
  370. void playlist_Command( playlist_t * p_playlist, int i_command, int i_arg )
  371. {
  372.     vlc_mutex_lock( &p_playlist->object_lock );
  373.  
  374.     switch( i_command )
  375.     {
  376.     case PLAYLIST_STOP:
  377.         p_playlist->i_status = PLAYLIST_STOPPED;
  378.         if( p_playlist->p_input )
  379.         {
  380.             input_StopThread( p_playlist->p_input );
  381.         }
  382.         break;
  383.  
  384.     case PLAYLIST_PLAY:
  385.         p_playlist->i_status = PLAYLIST_RUNNING;
  386.         if( p_playlist->p_input )
  387.         {
  388.             input_SetStatus( p_playlist->p_input, INPUT_STATUS_PLAY );
  389.         }
  390.         break;
  391.  
  392.     case PLAYLIST_PAUSE:
  393.         p_playlist->i_status = PLAYLIST_PAUSED;
  394.         if( p_playlist->p_input )
  395.         {
  396.             input_SetStatus( p_playlist->p_input, INPUT_STATUS_PAUSE );
  397.         }
  398.         break;
  399.  
  400.     case PLAYLIST_SKIP:
  401.         p_playlist->i_status = PLAYLIST_STOPPED;
  402.         SkipItem( p_playlist, i_arg );
  403.         if( p_playlist->p_input )
  404.         {
  405.             input_StopThread( p_playlist->p_input );
  406.         }
  407.         p_playlist->i_status = PLAYLIST_RUNNING;
  408.         break;
  409.  
  410.     case PLAYLIST_GOTO:
  411.         if( i_arg >= 0 && i_arg < p_playlist->i_size )
  412.         {
  413.             p_playlist->i_index = i_arg;
  414.             if( p_playlist->p_input )
  415.             {
  416.                 input_StopThread( p_playlist->p_input );
  417.             }
  418.             p_playlist->i_status = PLAYLIST_RUNNING;
  419.         }
  420.         break;
  421.  
  422.     default:
  423.         msg_Err( p_playlist, "unknown playlist command" );
  424.         break;
  425.     }
  426.  
  427.     vlc_mutex_unlock( &p_playlist->object_lock );
  428.  
  429.     return;
  430. }
  431.  
  432. /* Following functions are local */
  433.  
  434. /*****************************************************************************
  435.  * RunThread: main playlist thread
  436.  *****************************************************************************/
  437. static void RunThread ( playlist_t *p_playlist )
  438. {
  439.     /* Tell above that we're ready */
  440.     vlc_thread_ready( p_playlist );
  441.  
  442.     while( !p_playlist->b_die )
  443.     {
  444.         vlc_mutex_lock( &p_playlist->object_lock );
  445.  
  446.         /* If there is an input, check that it doesn't need to die. */
  447.         if( p_playlist->p_input )
  448.         {
  449.             /* This input is dead. Remove it ! */
  450.             if( p_playlist->p_input->b_dead )
  451.             {
  452.                 input_thread_t *p_input;
  453.  
  454.                 /* Unlink current input */
  455.                 p_input = p_playlist->p_input;
  456.                 p_playlist->p_input = NULL;
  457.                 vlc_object_detach( p_input );
  458.  
  459.                 /* Release the playlist lock, because we may get stuck
  460.                  * in input_DestroyThread() for some time. */
  461.                 vlc_mutex_unlock( &p_playlist->object_lock );
  462.  
  463.                 /* Destroy input */
  464.                 input_DestroyThread( p_input );
  465.                 vlc_object_destroy( p_input );
  466.                 continue;
  467.             }
  468.             /* This input is dying, let him do */
  469.             else if( p_playlist->p_input->b_die )
  470.             {
  471.                 ;
  472.             }
  473.             /* This input has finished, ask him to die ! */
  474.             else if( p_playlist->p_input->b_error
  475.                       || p_playlist->p_input->b_eof )
  476.             {
  477.                 /* Check for autodeletion */
  478.                 if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
  479.                 {
  480.                     vlc_mutex_unlock( &p_playlist->object_lock );
  481.                     playlist_Delete( p_playlist, p_playlist->i_index );
  482.                     vlc_mutex_lock( &p_playlist->object_lock );
  483.                 }
  484.  
  485.                 /* Select the next playlist item */
  486.                 SkipItem( p_playlist, 1 );
  487.  
  488.                 /* Release the playlist lock, because we may get stuck
  489.                  * in input_StopThread() for some time. */
  490.                 vlc_mutex_unlock( &p_playlist->object_lock );
  491.                 input_StopThread( p_playlist->p_input );
  492.                 continue;
  493.             }
  494.         }
  495.         else if( p_playlist->i_status != PLAYLIST_STOPPED )
  496.         {
  497.             PlayItem( p_playlist );
  498.         }
  499.  
  500.         vlc_mutex_unlock( &p_playlist->object_lock );
  501.  
  502.         msleep( INTF_IDLE_SLEEP );
  503.     }
  504.  
  505.     /* If there is an input, kill it */
  506.     while( 1 )
  507.     {
  508.         vlc_mutex_lock( &p_playlist->object_lock );
  509.  
  510.         if( p_playlist->p_input == NULL )
  511.         {
  512.             vlc_mutex_unlock( &p_playlist->object_lock );
  513.             break;
  514.         }
  515.  
  516.         if( p_playlist->p_input->b_dead )
  517.         {
  518.             input_thread_t *p_input;
  519.  
  520.             /* Unlink current input */
  521.             p_input = p_playlist->p_input;
  522.             p_playlist->p_input = NULL;
  523.             vlc_object_detach( p_input );
  524.             vlc_mutex_unlock( &p_playlist->object_lock );
  525.  
  526.             /* Destroy input */
  527.             input_DestroyThread( p_input );
  528.             vlc_object_destroy( p_input );
  529.             continue;
  530.         }
  531.         else if( p_playlist->p_input->b_die )
  532.         {
  533.             /* This input is dying, leave him alone */
  534.             ;
  535.         }
  536.         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
  537.         {
  538.             vlc_mutex_unlock( &p_playlist->object_lock );
  539.             input_StopThread( p_playlist->p_input );
  540.             continue;
  541.         }
  542.         else
  543.         {
  544.             p_playlist->p_input->b_eof = 1;
  545.         }
  546.  
  547.         vlc_mutex_unlock( &p_playlist->object_lock );
  548.  
  549.         msleep( INTF_IDLE_SLEEP );
  550.     }
  551. }
  552.  
  553. /*****************************************************************************
  554.  * SkipItem: go to Xth playlist item
  555.  *****************************************************************************
  556.  * This function calculates the position of the next playlist item, depending
  557.  * on the playlist course mode (forward, backward, random...).
  558.  *****************************************************************************/
  559. static void SkipItem( playlist_t *p_playlist, int i_arg )
  560. {
  561.     int i_oldindex = p_playlist->i_index;
  562.     vlc_bool_t b_random;
  563.  
  564.     /* If the playlist is empty, there is no current item */
  565.     if( p_playlist->i_size == 0 )
  566.     {
  567.         p_playlist->i_index = -1;
  568.         return;
  569.     }
  570.  
  571.     b_random = config_GetInt( p_playlist, "random" );
  572.  
  573.     /* Increment */
  574.     if( b_random )
  575.     {
  576.         srand( (unsigned int)mdate() );
  577.  
  578.         /* Simple random stuff - we cheat a bit to minimize the chances to
  579.          * get the same index again. */
  580.         i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
  581.         if( i_arg == 0 )
  582.         {
  583.             i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
  584.         }
  585.     }
  586.  
  587.     p_playlist->i_index += i_arg;
  588.  
  589.     /* Boundary check */
  590.     if( p_playlist->i_index >= p_playlist->i_size )
  591.     {
  592.         if( p_playlist->i_status == PLAYLIST_STOPPED
  593.              || b_random
  594.              || config_GetInt( p_playlist, "loop" ) )
  595.         {
  596.             p_playlist->i_index -= p_playlist->i_size
  597.                          * ( p_playlist->i_index / p_playlist->i_size );
  598.         }
  599.         else
  600.         {
  601.             /* Don't loop by default: stop at playlist end */
  602.             p_playlist->i_index = i_oldindex;
  603.             p_playlist->i_status = PLAYLIST_STOPPED;
  604.         }
  605.     }
  606.     else if( p_playlist->i_index < 0 )
  607.     {
  608.         p_playlist->i_index = p_playlist->i_size - 1;
  609.     }
  610. }
  611.  
  612. /*****************************************************************************
  613.  * PlayItem: play current playlist item
  614.  *****************************************************************************
  615.  * This function calculates the position of the next playlist item, depending
  616.  * on the playlist course mode (forward, backward, random...).
  617.  *****************************************************************************/
  618. static void PlayItem( playlist_t *p_playlist )
  619. {
  620.     if( p_playlist->i_index == -1 )
  621.     {
  622.         if( p_playlist->i_size == 0 )
  623.         {
  624.             return;
  625.         }
  626.  
  627.         SkipItem( p_playlist, 1 );
  628.     }
  629.  
  630.     msg_Dbg( p_playlist, "creating new input thread" );
  631.     p_playlist->p_input = input_CreateThread( p_playlist,
  632.                                   p_playlist->pp_items[p_playlist->i_index] );
  633. }
  634.  
  635. /*****************************************************************************
  636.  * Poubellize: put an input thread in the trashcan
  637.  *****************************************************************************
  638.  * XXX: unused
  639.  *****************************************************************************/
  640. static void Poubellize ( playlist_t *p_playlist, input_thread_t *p_input )
  641. {
  642.     msg_Dbg( p_playlist, "poubellizing input %i\n", p_input->i_object_id );
  643. }
  644.  
  645. /*****************************************************************************
  646.  * playlist_LoadFile: load a playlist file.
  647.  ****************************************************************************/
  648. int playlist_LoadFile( playlist_t * p_playlist, const char *psz_filename )
  649. {
  650.     FILE *file;
  651.     char line[1024];
  652.     int i_current_status;
  653.     int i;
  654.  
  655.     msg_Dbg( p_playlist, "opening playlist file %s", psz_filename );
  656.  
  657.     file = fopen( psz_filename, "rt" );
  658.     if( !file )
  659.     {
  660.         msg_Err( p_playlist, "playlist file %s does not exist", psz_filename );
  661.         return -1;
  662.     }
  663.     fseek( file, 0L, SEEK_SET );
  664.  
  665.     /* check the file is not empty */
  666.     if ( ! fgets( line, 1024, file ) )
  667.     {
  668.         msg_Err( p_playlist, "playlist file %s is empty", psz_filename );
  669.         fclose( file );
  670.         return -1;
  671.     }
  672.  
  673.     /* get rid of line feed */
  674.     if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' )
  675.     {
  676.        line[strlen(line)-1] = (char)0;
  677.        if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0;
  678.     }
  679.     /* check the file format is valid */
  680.     if ( strcmp ( line , PLAYLIST_FILE_HEADER_0_5 ) )
  681.     {
  682.         msg_Err( p_playlist, "playlist file %s format is unsupported"
  683.                 , psz_filename );
  684.         fclose( file );
  685.         return -1;
  686.     }
  687.  
  688.     /* stop playing */
  689.     i_current_status = p_playlist->i_status;
  690.     if ( p_playlist->i_status != PLAYLIST_STOPPED )
  691.     {
  692.         playlist_Stop ( p_playlist );
  693.     }
  694.  
  695.     /* delete current content of the playlist */
  696.     for( i = p_playlist->i_size - 1; i >= 0; i-- )
  697.     {
  698.         playlist_Delete ( p_playlist , i );
  699.     }
  700.  
  701.     /* simply add each line */
  702.     while( fgets( line, 1024, file ) )
  703.     {
  704.        /* ignore comments or empty lines */
  705.        if( (line[0] == '#') || (line[0] == '\r') || (line[0] == '\n')
  706.                || (line[0] == (char)0) )
  707.            continue;
  708.  
  709.        /* get rid of line feed */
  710.        if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' )
  711.        {
  712.            line[strlen(line)-1] = (char)0;
  713.            if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0;
  714.        }
  715.  
  716.        playlist_Add ( p_playlist , (char*) &line , PLAYLIST_APPEND , PLAYLIST_END );
  717.     }
  718.  
  719.     /* start playing */
  720.     if ( i_current_status != PLAYLIST_STOPPED )
  721.     {
  722.         playlist_Play ( p_playlist );
  723.     }
  724.  
  725.     fclose( file );
  726.  
  727.     return 0;
  728. }
  729.  
  730. /*****************************************************************************
  731.  * playlist_SaveFile: Save a playlist in a file.
  732.  *****************************************************************************/
  733. int playlist_SaveFile( playlist_t * p_playlist, const char * psz_filename )
  734. {
  735.     FILE *file;
  736.     int i;
  737.  
  738.     vlc_mutex_lock( &p_playlist->object_lock );
  739.  
  740.     msg_Dbg( p_playlist, "saving playlist file %s", psz_filename );
  741.  
  742.     file = fopen( psz_filename, "wt" );
  743.     if( !file )
  744.     {
  745.         msg_Err( p_playlist , "could not create playlist file %s"
  746.                 , psz_filename );
  747.         return -1;
  748.     }
  749.  
  750.     fprintf( file , PLAYLIST_FILE_HEADER_0_5 PLAYLIST_FILE_EOL );
  751.  
  752.     for ( i = 0 ; i < p_playlist->i_size ; i++ )
  753.     {
  754.         fprintf( file , p_playlist->pp_items[i]->psz_uri );
  755.         fprintf( file , PLAYLIST_FILE_EOL );
  756.     }
  757.  
  758.     fclose( file );
  759.  
  760.     vlc_mutex_unlock( &p_playlist->object_lock );
  761.  
  762.     return 0;
  763. }
  764.